package cheme.ProcessEditor.Policies;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editpolicies.XYLayoutEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;

import cheme.ProcessEditor.Parts.ITerminalPart;
import cheme.ProcessModel.IChild;
import cheme.ProcessModel.Cmds.AddCmd;
import cheme.ProcessModel.Cmds.ChangeLocationCmd;
import cheme.ProcessModel.Cmds.MoveCmd;
import cheme.ProcessModel.Cmds.ReconnectStreamCmd;
import cheme.ProcessModel.ProcessUnits.ITerminal;
import cheme.ProcessModel.ProcessUnits.Mixer;
import cheme.ProcessModel.ProcessUnits.ProcessUnit;
import cheme.ProcessModel.ProcessUnits.Separator;
import cheme.ProcessModel.ProcessUnits.SplitJoin;
import cheme.ProcessModel.ProcessUnits.TerminalSink;
import cheme.ProcessModel.ProcessUnits.TerminalSource;
import cheme.ProcessModel.ProcessUnits.Streams.AbstractStream;
import cheme.ProcessModel.Visitors.ReflectiveVisitor;

public class FigureDropPolicy extends XYLayoutEditPolicy
{
	@Override
	protected Command getAddCommand(Request generic)
	{
		CompoundCommand cmd = null;
		
		if(generic instanceof ChangeBoundsRequest)
		{
			if(!(getHost().getModel() instanceof ProcessUnit))
			{
				return null;
			}
			
			ProcessUnit newProcessUnit = (ProcessUnit)getHost().getModel();
			
			/*
			 * Only process the first units dropped
			 * 
			 * Note that there is no need to explicitly delete the terminal as
			 * the final reconnect will initiate the implicit deletion of the 
			 * unused terminal.
			 */
			if(((ChangeBoundsRequest)generic).getEditParts().size() == 1)
			{
				cmd = new CompoundCommand();
				Command temp;
				
				GraphicalEditPart sourcePart = (GraphicalEditPart)((ChangeBoundsRequest)generic).getEditParts().get(0);
				
				if(!(sourcePart.getModel() instanceof ProcessUnit))
				{
					return null;
				}
				
				ProcessUnit oldProcessUnit = (ProcessUnit)sourcePart.getModel();
				
				boolean oldUnitIsTerminal = (oldProcessUnit instanceof TerminalSource) || (oldProcessUnit instanceof TerminalSink);
				boolean newUnitIsTerminal = (newProcessUnit instanceof TerminalSource) || (newProcessUnit instanceof TerminalSink);
				
//				if(oldUnitIsTerminal && newUnitIsTerminal)
//				{
//					/*
//					 * Does not work because the unit that is to be kept 
//					 * ends up orphaned form the processmodel
//					 */
//					temp = mergeUnits(oldProcessUnit, newProcessUnit);
//					cmd.add(new AddCmd(oldProcessUnit, newProcessUnit.getParent()));
//					cmd.add(temp);
//				}
//				else 
				if(!oldUnitIsTerminal && newUnitIsTerminal)
				{
					/*
					 * Does not work because the old unit that is to be kept 
					 * ends up orphaned form the processmodel
					 */
					temp = mergeUnits(newProcessUnit, oldProcessUnit);
					cmd.add(new AddCmd(oldProcessUnit, oldProcessUnit.getParent())); 
					//JB Change: this was adding newProcessUnit to the parent (the process model)
					cmd.add(new ChangeLocationCmd(oldProcessUnit, newProcessUnit.getLocation()));
					cmd.add(temp);
				}
				else if(oldUnitIsTerminal && !newUnitIsTerminal)
				{
					temp = mergeUnits(oldProcessUnit, newProcessUnit);
					cmd.add(temp);
				}
//				else if(!oldUnitIsTerminal && !newUnitIsTerminal)
//				{
//					temp = null;
//					temp = mergeUnits(oldProcessUnit, newProcessUnit);
//					
//					/*
//					 * This one's a little more tricky. If the first order fails, give the second order a try?
//					 */
//					if(null == temp)
//					{
//						temp = mergeUnits(newProcessUnit, oldProcessUnit);
//						cmd.add(new AddCmd(newProcessUnit, oldProcessUnit.getParent()));
//						cmd.add(temp);
//					}
//					else
//					{
//						cmd.add(new AddCmd(oldProcessUnit, newProcessUnit.getParent()));
//						cmd.add(temp);
//					}
//				}
			}
		}
		return cmd;
	}
	
	protected Command mergeUnits(ProcessUnit from, ProcessUnit to)
	{
		CompoundCommand cmd = null;
		
		int ins = from.getInStreams().size();
		int outs = from.getOutStreams().size();
		
		boolean inOK = to.willAcceptInStream(ins);
		boolean outOK = to.willAcceptOutStream(outs);
		
		if(inOK && outOK)
		{
			cmd = new CompoundCommand();
			ReconnectStreamCmd reconnect = null;
			
			for(IChild stream : from.getOutStreams())
			{
				reconnect = new ReconnectStreamCmd();
				reconnect.setNewSource(to);
				reconnect.setStream(((AbstractStream)stream));
				cmd.add(reconnect);
			}
			
			for(IChild stream : from.getInStreams())
			{
				reconnect = new ReconnectStreamCmd();
				reconnect.setNewSink(to);
				reconnect.setStream(((AbstractStream)stream));
				cmd.add(reconnect);
			}
		}
		
		return cmd.unwrap();
	}
	
	@Override
	protected Command createChangeConstraintCommand(EditPart child, Object constraint)
	{
		return null;
	}

	@Override
	protected Command getCreateCommand(CreateRequest request)
	{
		return null;
	}

}
